/* freeircd modes.c
 *
 * Iniciado em 23/08/2006
 */

#define _GNU_SOURCE
#include <errno.h>
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <time.h>
#include <stdarg.h>

#include "types.h"
#include "configuration.h"
#include "getinfo.h"
#include "alloc.h"

#include "debug.h"

#define M_SET 1
#define M_UNSET 0
#define M_DEFAULT -1

void user_modes(CLIENTS *, long *);
void chan_modes(CLIENTS *, CHANS *);
bool is_banned(CLIENTS *, CHANS *);

static void set_ban(CLIENTS *, CHANS *, char *, char *);
void unset_ban(CLIENTS *, CHANS *, char *, char *, bool);
static bool is_already_banned(struct ban_list *, veryshort, char *, char *);

extern void write_sock(CLIENTS *, char *, ...);
extern void send_to_all_in_chan(CLIENTS *client, char *chan, char *str, ...);
extern inline void memerror(void *);
extern veryshort get_user_access_level(struct access_users *, char *);


void user_modes(CLIENTS *client, long *ircops)
{
        char *nick;
        char *modes;
        int mtype=M_DEFAULT;
        bool show=false;

        if (tknum(client->msg,' ') < 3)
                show=true;

        nick=getoken(client->msg,2,' ');

        if (!compic(client->nick,nick))
        {
                /*:irc.ptsida.org 502 coiso :Cant change mode for other users*/
                write_sock(client,":%s 502 %s :Cant change mode for other users\r\n",SERVER_NAME,client->nick);
                free(nick);
                return;
        }
        free(nick);

        if (show)
        {
                /*:irc.ptsida.org 221 coiso +io*/
                write_sock(client,":%s 221 %s +i%s\r\n",SERVER_NAME,client->nick,(client->ircop) ? "o" : "");
        }
        else
        {
                modes=getoken(client->msg,3,' ');

                /*Ignorar:
                        +-i
                        +-w
                        +-s
                        +o
                */

                if (strlen(modes) >= 1)
                {
                        if (modes[0] == '+')
                                mtype=M_SET;
                        if (modes[0] == '-')
                                mtype=M_UNSET;

                        if (mtype != M_DEFAULT)
                        {
                                int counter=1;

                                while (modes[counter] != '\0')
                                {
                                        switch (modes[counter])
                                        {
                                                case '+': mtype=M_SET; break;
                                                case '-': mtype=M_UNSET; break;
                                                case 'i': break;
                                                case 's': break;
                                                case 'w': break;
                                                case 'o':
                                                        {
                                                                if (mtype == M_UNSET)
                                                                {
                                                                        if (client->ircop)
                                                                        {
                                                                                client->ircop=false;
                                                                                /*:coiso!~orium@irc.ptsida.org MODE coiso :-o*/
                                                                                write_sock(client,":%s!%s@%s MODE %s :-o\r\n",
                                                                                        client->nick,USER_NAME,client->md5_ip,client->nick);
                                                                                (*ircops)--;
                                                                        }
                                                                }

                                                                break;
                                                        }
                                                default:
                                                        {
                                                                /*:irc.ptsida.org 501 coiso :Unknown MODE flag*/
                                                                write_sock(client,":%s 501 %s :Unknown MODE flag\r\n",
                                                                                  SERVER_NAME,client->nick);
                                                                free(modes);
                                                                return;
                                                        }
                                        }
                                        counter++;
                                }
                        }
                }
                free(modes);
        }
}

void chan_modes(CLIENTS *client, CHANS *chans)
{
        char *chan;
        char *modes;
        char *key;
        int mtype=M_DEFAULT;
        bool show=false;
        struct user_chans_list *usr_chn_lst;

        if (tknum(client->msg,' ') < 3)
                show=true;

        chan=getoken(client->msg,2,' ');
        usr_chn_lst=get_chan_from_user_list(client->chans,chan);

        if (usr_chn_lst == NULL)
        {
                /*:irc.ptsida.org 403 coiso #coisos :No such channel*/
                write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan);
                free(chan);
                return;
        }

        if (tknum(client->msg,' ') == 3)
        {
                modes=getoken(client->msg,3,' ');
                if (comp(modes,"+b"))
                {
                        struct ban_list *pbans;
                        /*
                        >> :irc.ptsida.org 367 coiso #new coisoo!*@* coiso 1156368861
                        >> :irc.ptsida.org 367 coiso #new #new!*@* coiso 1156368509
                        >> :irc.ptsida.org 368 coiso #new :End of channel ban list
                        */
                        pbans=usr_chn_lst->chan->bans;
                        while (pbans != NULL)
                        {
                                if (pbans->mask_type == MASK_NICK)
                                        write_sock(client,":%s 367 %s %s %s!*@* %s %ld\r\n",SERVER_NAME,client->nick,chan,pbans->nick,
                                                                                            pbans->banned_by,pbans->banned_at);
                                if (pbans->mask_type == MASK_IP)
                                        write_sock(client,":%s 367 %s %s *!*@%s %s %ld\r\n",SERVER_NAME,client->nick,chan,pbans->md5_ip,
                                                                                            pbans->banned_by,pbans->banned_at);
                                if (pbans->mask_type == MASK_BOTH)
                                        write_sock(client,":%s 367 %s %s %s!*@%s %s %ld\r\n",SERVER_NAME,client->nick,chan,pbans->nick,
                                                                                           pbans->md5_ip,pbans->banned_by,pbans->banned_at);
                                if (pbans->mask_type == MASK_EVERYBODY)
                                        write_sock(client,":%s 367 %s %s *!*@* %s %ld\r\n",SERVER_NAME,client->nick,chan,pbans->banned_by,
                                                                                           pbans->banned_at);

                                pbans=pbans->next;
                        }

                        write_sock(client,":%s 368 %s %s :End of channel ban list\r\n",SERVER_NAME,client->nick,chan);
                        free(chan);
                        free(modes);
                        return;
                }

                free(modes);
        }

        if (show)
        {
                /*
                :irc.ptsida.org 324 coiso #coiso +nt
                :irc.ptsida.org 329 coiso #coiso 1156345319
                */
                write_sock(client,":%s 324 %s %s +n%s%s%s%s%s%s\r\n",SERVER_NAME,client->nick,chan,
                           (usr_chn_lst->chan->modes.t) ? "t" : "",(usr_chn_lst->chan->modes.m) ? "m" : "",
                           (usr_chn_lst->chan->modes.s) ? "s" : "",(usr_chn_lst->chan->modes.c) ? "c" : "",
                           (usr_chn_lst->chan->modes.key != NULL) ? "k " : "",
                           (usr_chn_lst->chan->modes.key != NULL) ? usr_chn_lst->chan->modes.key : "");
                write_sock(client,":%s 329 %s %s %ld\r\n",SERVER_NAME,client->nick,chan,usr_chn_lst->chan->created);
        }
        else
        {
                struct user_chans_list *usr_chn_lst2;

                modes=getoken(client->msg,3,' ');

                if (!usr_chn_lst->op && !client->ircop)
                {
                        /*:irc.ptsida.org 482 coiso #coiso :You're not channel operator*/
                        write_sock(client,":%s 482 %s %s :You're not channel operator\r\n",SERVER_NAME,client->nick,chan);
                        free(chan);
                        free(modes);
                        return;
                }

                if (strlen(modes) >= 1)
                {
                        int target=4;
                        char *tnick;
                        char *ban_mask;

                        if (modes[0] == '+')
                                mtype=M_SET;
                        if (modes[0] == '-')
                                mtype=M_UNSET;

                        if (mtype != M_DEFAULT)
                        {
                                int counter=1;
                                struct chan_users_list *chn_usr_lst;

                                while (modes[counter] != '\0')
                                {
                                        switch (modes[counter])
                                        {
                                                case '+': mtype=M_SET; break;
                                                case '-': mtype=M_UNSET; break;
                                                case 'n': break;
                                                case 'l': target++; break;
                                                case 'p': break;
                                                case 'i': break;
                                                case 'm':
                                                {
                                                        if (usr_chn_lst->chan->reg != NULL && usr_chn_lst->chan->reg->set.mode_lock) {
                                                                write_sock(client,":%s!%s@%s NOTICE %s :Mlock on\r\n",
                                                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                                                break;
                                                        }

                                                        /*:coiso!~orium@irc.ptsida.org MODE #hehe +m*/
                                                        write_sock(client,":%s!%s@%s MODE %s %cm\r\n",
                                                                          client->nick,USER_NAME,client->md5_ip,chan,
                                                                          (mtype == M_SET) ? '+' : '-');

                                                        send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s %cm\r\n",
                                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                                        (mtype == M_SET) ? '+' : '-');

                                                        if (mtype == M_SET)
                                                                usr_chn_lst->chan->modes.m=true;
                                                        else
                                                                usr_chn_lst->chan->modes.m=false;

                                                        break;
                                                }
                                                case 'c':
                                                {
                                                        if (usr_chn_lst->chan->reg != NULL && usr_chn_lst->chan->reg->set.mode_lock) {
                                                                write_sock(client,":%s!%s@%s NOTICE %s :Mlock on\r\n",
                                                                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                                                break;
                                                        }

                                                        /*:coiso!~orium@irc.ptsida.org MODE #hehe +m*/
                                                        write_sock(client,":%s!%s@%s MODE %s %cc\r\n",
                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                        (mtype == M_SET) ? '+' : '-');

                                                        send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s %cc\r\n",
                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                        (mtype == M_SET) ? '+' : '-');

                                                        if (mtype == M_SET)
                                                                usr_chn_lst->chan->modes.c=true;
                                                        else
                                                                usr_chn_lst->chan->modes.c=false;

                                                        break;
                                                }
                                                case 't':
                                                {
                                                        if (usr_chn_lst->chan->reg != NULL && usr_chn_lst->chan->reg->set.mode_lock) {
                                                                write_sock(client,":%s!%s@%s NOTICE %s :Mlock on\r\n",
                                                                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                                                break;
                                                        }

                                                        /*:coiso!~orium@irc.ptsida.org MODE #hehe +t*/
                                                        write_sock(client,":%s!%s@%s MODE %s %ct\r\n",
                                                                          client->nick,USER_NAME,client->md5_ip,chan,
                                                                          (mtype == M_SET) ? '+' : '-');

                                                        send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s %ct\r\n",
                                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                                        (mtype == M_SET) ? '+' : '-');

                                                        if (mtype == M_SET)
                                                                usr_chn_lst->chan->modes.t=true;
                                                        else
                                                                usr_chn_lst->chan->modes.t=false;

                                                        break;
                                                }
                                                case 's':
                                                {
                                                        if (usr_chn_lst->chan->reg != NULL && usr_chn_lst->chan->reg->set.mode_lock) {
                                                                write_sock(client,":%s!%s@%s NOTICE %s :Mlock on\r\n",
                                                                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                                                break;
                                                        }

                                                        /*:coiso!~orium@irc.ptsida.org MODE #hehe +s*/
                                                        write_sock(client,":%s!%s@%s MODE %s %cs\r\n",
                                                                          client->nick,USER_NAME,client->md5_ip,chan,
                                                                          (mtype == M_SET) ? '+' : '-');

                                                        send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s %cs\r\n",
                                                                            client->nick,USER_NAME,client->md5_ip,chan,
                                                                            (mtype == M_SET) ? '+' : '-');

                                                        if (mtype == M_SET)
                                                                usr_chn_lst->chan->modes.s=true;
                                                        else
                                                                usr_chn_lst->chan->modes.s=false;

                                                        break;
                                                }
                                                case 'o':
                                                {
                                                        chn_usr_lst=usr_chn_lst->chan->users;

                                                        tnick=getoken(client->msg,target,' ');
                                                        if (tnick != NULL)
                                                        {
                                                                chn_usr_lst=get_user_in_chan(usr_chn_lst->chan,tnick);

                                                                if (chn_usr_lst == NULL)
                                                                {
                                                                        /*:irc.ptsida.org 401 coiso c :No such nick*/
                                                                        write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,
                                                                                                                        client->nick,tnick);
                                                                        free(tnick);
                                                                        break;
                                                                }

                                                                usr_chn_lst2=get_chan_from_user_list(chn_usr_lst->user->chans,chan);

                                                                if (usr_chn_lst2->chan->reg != NULL
                                                                    && get_user_access_level(usr_chn_lst2->chan->reg->ausers,tnick)
                                                                       <= usr_chn_lst2->chan->reg->levels.autodeop
                                                                    && mtype == M_SET)
                                                                {
                                                                        write_sock(client,":%s!%s@%s NOTICE %s :O nivel de acesso do user"
                                                                                " %s ao canal %s não lhe permite ter op\r\n",
                                                                               CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan);
                                                                        free(tnick);
                                                                        break;
                                                                }

                                                                if (usr_chn_lst2->chan->reg != NULL
                                                                    && usr_chn_lst2->chan->reg->set.secure_ops
                                                                    && get_user_access_level(usr_chn_lst2->chan->reg->ausers,tnick) <= 0
                                                                    && mtype == M_SET)
                                                                {
                                                                        write_sock(client,":%s!%s@%s NOTICE %s :O nivel de acesso do user"
                                                                                " %s ao canal %s não lhe permite ter op (secureops)\r\n",
                                                                               CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,tnick,chan);
                                                                        free(tnick);
                                                                        break;
                                                                }


                                                                if (mtype == M_UNSET && chn_usr_lst->user->ircop
                                                                    && !compic(tnick,client->nick))
                                                                        write_sock(client,
                                                                        ":%s 401 %s %s :Cannot deop an ircop\r\n",
                                                                        SERVER_NAME,client->nick,tnick);
                                                                        /*:irc.putedo.org 401 coiso coiso :Cannot deop an ircop*/
                                                                else
                                                                {
                                                                        /*:coiso!~orium@irc.ptsida.org MODE #hehe +o nick*/
                                                                        if ((mtype == M_SET && !usr_chn_lst2->op)
                                                                            || (mtype == M_UNSET && usr_chn_lst2->op))
                                                                        {
                                                                                write_sock(client,":%s!%s@%s MODE %s %co %s\r\n",
                                                                                                client->nick,USER_NAME,client->md5_ip,chan,
                                                                                                (mtype == M_SET) ? '+' : '-',tnick);

                                                                                send_to_all_in_chan(client,chan,
                                                                                                        ":%s!%s@%s MODE %s %co %s\r\n",
                                                                                                client->nick,USER_NAME,client->md5_ip,
                                                                                                chan,
                                                                                                (mtype == M_SET) ? '+' : '-',tnick);
                                                                        }

                                                                        if (mtype == M_SET)
                                                                        {
                                                                                usr_chn_lst2->op=true; /*do nick*/
                                                                                chn_usr_lst->op=true; /*do chan*/

                                                                                usr_chn_lst2->voice=false; /*do nick*/
                                                                                chn_usr_lst->voice=false; /*do chan*/
                                                                        }
                                                                        else
                                                                        {
                                                                                usr_chn_lst2->op=false; /*do nick*/
                                                                                chn_usr_lst->op=false; /*do chan*/
                                                                        }
                                                                }

                                                                target++;
                                                        }
                                                        free(tnick);
                                                        break;
                                                }
                                                case 'v':
                                                {
                                                        chn_usr_lst=usr_chn_lst->chan->users;

                                                        tnick=getoken(client->msg,target,' ');
                                                        if (tnick != NULL)
                                                        {
                                                                chn_usr_lst=get_user_in_chan(usr_chn_lst->chan,tnick);

                                                                if (chn_usr_lst == NULL)
                                                                {
                                                                        /*:irc.ptsida.org 401 coiso c :No such nick*/
                                                                        write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,
                                                                                client->nick,tnick);
                                                                        free(tnick);
                                                                        break;
                                                                }

                                                                usr_chn_lst2=get_chan_from_user_list(chn_usr_lst->user->chans,chan);

                                                                /*:coiso!~orium@irc.ptsida.org MODE #hehe +v nick*/
                                                                if ((mtype == M_SET && !usr_chn_lst2->voice)
                                                                        || (mtype == M_UNSET && usr_chn_lst2->voice))
                                                                {
                                                                        write_sock(client,":%s!%s@%s MODE %s %cv %s\r\n",
                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                        (mtype == M_SET) ? '+' : '-',tnick);

                                                                        send_to_all_in_chan(client,chan,
                                                                        ":%s!%s@%s MODE %s %cv %s\r\n",
                                                                        client->nick,USER_NAME,client->md5_ip,chan,
                                                                        (mtype == M_SET) ? '+' : '-',tnick);
                                                                }

                                                                if (mtype == M_SET)
                                                                {
                                                                        usr_chn_lst2->voice=true; /*do nick*/
                                                                        chn_usr_lst->voice=true; /*do chan*/
                                                                }
                                                                else
                                                                {
                                                                        usr_chn_lst2->voice=false; /*do nick*/
                                                                        chn_usr_lst->voice=false; /*do chan*/
                                                                }

                                                                target++;
                                                        }
                                                        free(tnick);
                                                        break;
                                                }
                                                case 'k':
                                                {
                                                        if (usr_chn_lst->chan->reg != NULL && usr_chn_lst->chan->reg->set.mode_lock) {
                                                                write_sock(client,":%s!%s@%s NOTICE %s :Mlock on\r\n",
                                                                                CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                                                break;
                                                        }

                                                        if (mtype == M_SET)
                                                        {
                                                                key=getoken(client->msg,target,' ');
                                                                if (key != NULL)
                                                                {
                                                                        write_sock(client,":%s!%s@%s MODE %s +k %s\r\n",
                                                                                          client->nick,USER_NAME,client->md5_ip,chan,key);

                                                                        send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s +k %s\r\n",
                                                                                            client->nick,USER_NAME,client->md5_ip,chan,key);

                                                                        usr_chn_lst->chan->modes.key=key;

                                                                        target++;
                                                                }
                                                        }
                                                        else
                                                        {
                                                                write_sock(client,":%s!%s@%s MODE %s -k\r\n",
                                                                                  client->nick,USER_NAME,client->md5_ip,chan);

                                                                send_to_all_in_chan(client,chan,":%s!%s@%s MODE %s -k\r\n",
                                                                                    client->nick,USER_NAME,client->md5_ip,chan);

                                                                free(usr_chn_lst->chan->modes.key);
                                                                usr_chn_lst->chan->modes.key=NULL;
                                                        }
                                                        break;
                                                }
                                                case 'b':
                                                {
                                                        ban_mask=getoken(client->msg,target,' ');
                                                        if (ban_mask != NULL)
                                                        {
                                                                if (tknum(client->msg,' ') > 3)
                                                                {
                                                                        if (mtype == M_SET)
                                                                                set_ban(client,usr_chn_lst->chan,ban_mask,chan);
                                                                        else
                                                                                unset_ban(client,usr_chn_lst->chan,ban_mask,chan,false);
                                                                }

                                                                free(ban_mask);
                                                                target++;
                                                        }
                                                        break;
                                                }
                                                default:
                                                {
                                                        /*:irc.ptsida.org 501 coiso :Unknown MODE flag*/
                                                        /*write_sock(client,":%s 501 %s :Unknown MODE flag\r\n",SERVER_NAME,client->nick);*/
                                                        free(modes);
                                                        free(chan);
                                                        return;
                                                }
                                        }
                                        counter++;
                                }
                        }
                }
                free(modes);
        }

        free(chan);
}

static void set_ban(CLIENTS *client, CHANS *chan, char *ban_mask, char *chan_name)
{
        struct ban_list *pban;
        int num_u=0;
        int num_ip=0;
        int counter=0;
        veryshort mask_type=-1;
        char *nick=NULL;
        char *s_ip=NULL;

        while (ban_mask[counter] != '\0')
        {
                if (ban_mask[counter] == '!') num_u++;
                if (ban_mask[counter] == '@') num_ip++;

                counter++;
        }

        if (num_u > 1 || num_ip > 1)
                return;

        if ((!num_u && num_ip) || (num_u && !num_ip))
                return;

        if (!num_u && !num_ip)
        {
                nick=malloc(strlen(ban_mask)+1);
                memerror(nick);
                strcpy(nick,ban_mask);
                mask_type=MASK_NICK;
        }
        else if (num_u && num_ip)
        {

                nick=getoken(ban_mask,1,'!');
                if (comp(nick,"*"))
                {
                        free(nick);
                        nick=NULL;
                }

                s_ip=getoken(ban_mask,2,'@');
                if (comp(s_ip,"*"))
                {
                        free(s_ip);
                        s_ip=NULL;
                }

                if (s_ip != NULL && nick != NULL)
                        mask_type=MASK_BOTH;
                else if (nick != NULL && s_ip == NULL)
                        mask_type=MASK_NICK;
                else if (nick == NULL && s_ip != NULL)
                        mask_type=MASK_IP;
                else if (nick == NULL && s_ip == NULL)
                        mask_type=MASK_EVERYBODY;
        }
        else
                return;

        if (mask_type == -1)
        {
                free(nick);
                free(s_ip);
                return;
        }

        if (nick != NULL)
        {
                counter=0;
                while (nick[counter] != '\0')
                {
                        if ((nick[counter] >= 'A' && nick[counter] <= 'Z')
                            || (nick[counter] >= 'a' && nick[counter] <= 'z')
                            || (nick[counter] >= '0' && nick[counter] <= '9')
                            || nick[counter] == '`'
                            || nick[counter] == '\\'
                            || nick[counter] == '['
                            || nick[counter] == ']'
                            || nick[counter] == '^'
                            || nick[counter] == '|'
                            || nick[counter] == '{'
                            || nick[counter] == '}'
                            || nick[counter] == '_'
                            || nick[counter] == '-')
                                ;
                        else /*nick mask invalida*/
                        {
                                free(nick);
                                free(s_ip);
                                return;
                        }

                        counter++;
                }
        }

        if (s_ip != NULL)
        {
                int tmp_counter=0;

                if (strlen(s_ip) != 32)
                {
                        free(nick);
                        free(s_ip);
                        return;
                }

                while (s_ip[tmp_counter] != '\0')
                {
                        if ((s_ip[tmp_counter] >= '0' && s_ip[tmp_counter] <= '9')
                             || (s_ip[tmp_counter] >= 'a' && s_ip[tmp_counter] <= 'f'))
                                ;
                        else
                        {
                                free(nick);
                                free(s_ip);
                                return;
                        }

                        tmp_counter++;
                }
        }

        if (is_already_banned(chan->bans,mask_type,nick,s_ip))
        {
                free(nick);
                free(s_ip);
                return;
        }

        pban=alloc_ban_list(&chan->bans);

        pban->nick=nick;
        pban->md5_ip=s_ip;
        pban->mask_type=mask_type;

        pban->banned_by=malloc(strlen(client->nick)+1);
        memerror(pban->banned_by);
        strcpy(pban->banned_by,client->nick);
        time(&(pban->banned_at));

        /*:coiso!~orium@irc.ptsida.org MODE #new +b coisoo!*@**/
        if (mask_type == MASK_NICK)
        {
                write_sock(client,":%s!%s@%s MODE %s +b %s!*@*\r\n",client->nick,USER_NAME,client->md5_ip,chan_name,pban->nick);
                send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s +b %s!*@*\r\n",client->nick,USER_NAME,client->md5_ip,
                                                                                       chan_name,pban->nick);
        }
        if (mask_type == MASK_IP)
        {
                write_sock(client,":%s!%s@%s MODE %s +b *!*@%s\r\n",client->nick,USER_NAME,client->md5_ip,chan_name,pban->md5_ip);
                send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s +b *!*@%s\r\n",client->nick,USER_NAME,client->md5_ip,
                                                                                       chan_name,pban->md5_ip);
        }
        if (mask_type == MASK_BOTH)
        {
                write_sock(client,":%s!%s@%s MODE %s +b %s!*@%s\r\n",client->nick,USER_NAME,client->md5_ip,chan_name,pban->nick,
                                                                     pban->md5_ip);
                send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s +b %s!*@%s\r\n",client->nick,USER_NAME,client->md5_ip,
                                                                                        chan_name,pban->nick,pban->md5_ip);
        }
        if (mask_type == MASK_EVERYBODY)
        {
                write_sock(client,":%s!%s@%s MODE %s +b *!*@*\r\n",client->nick,USER_NAME,client->md5_ip,chan_name);
                send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s +b *!*@*\r\n",client->nick,USER_NAME,client->md5_ip,chan_name);
        }
}

void unset_ban(CLIENTS *client, CHANS *chan, char *ban_mask, char *chan_name, bool chanserv)
{
        struct ban_list *pban;
        int num_u=0;
        int num_ip=0;
        int counter=0;
        veryshort mask_type=-1;
        char *nick=NULL;
        char *s_ip=NULL;
        bool tofree=false;

        while (ban_mask[counter] != '\0')
        {
                if (ban_mask[counter] == '!') num_u++;
                if (ban_mask[counter] == '@') num_ip++;
                counter++;
        }

        if (num_u > 1 || num_ip > 1)
                return;

        if ((!num_u && num_ip) || (num_u && !num_ip))
                return;

        if (!num_u && !num_ip)
        {
                nick=malloc(strlen(ban_mask)+1);
                memerror(nick);
                strcpy(nick,ban_mask);
                mask_type=MASK_NICK;
        }
        else if (num_u && num_ip)
        {

                nick=getoken(ban_mask,1,'!');
                if (comp(nick,"*"))
                {
                        free(nick);
                        nick=NULL;
                }

                s_ip=getoken(ban_mask,2,'@');
                if (comp(s_ip,"*"))
                {
                        free(s_ip);
                        s_ip=NULL;
                }

                if (s_ip != NULL && nick != NULL)
                        mask_type=MASK_BOTH;
                else if (nick != NULL && s_ip == NULL)
                        mask_type=MASK_NICK;
                else if (nick == NULL && s_ip != NULL)
                        mask_type=MASK_IP;
                else if (nick == NULL && s_ip == NULL)
                        mask_type=MASK_EVERYBODY;
        }
        else
                return;

        if (mask_type == -1)
        {
                free(nick);
                free(s_ip);
                return;
        }

        pban=chan->bans;

        while (pban != NULL)
        {
                if (mask_type == MASK_NICK && compic(pban->nick,nick))
                        tofree=true;

                if (mask_type == MASK_IP && compic(pban->md5_ip,s_ip))
                        tofree=true;

                if (mask_type == MASK_BOTH && compic(pban->md5_ip,s_ip) && compic(pban->nick,nick))
                        tofree=true;

                if (mask_type == MASK_EVERYBODY && pban->mask_type == MASK_EVERYBODY)
                        tofree=true;

                if (tofree)
                {
                        rm_ban_list(&chan->bans,pban);

                        free(pban->nick);
                        free(pban->md5_ip);
                        free(pban->banned_by);
                        free(pban);

                        break;
                }

                pban=pban->next;
        }

        if (tofree)
        {
                if (mask_type == MASK_NICK)
                {
                        write_sock(client,":%s!%s@%s MODE %s -b %s!*@*\r\n",(chanserv) ? CHANSERV_NAME : client->nick,USER_NAME,(chanserv) ? SERVER_NAME : client->md5_ip,chan_name,nick);
                        send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s -b %s!*@*\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,chan_name,nick);
                }
                if (mask_type == MASK_IP)
                {
                        write_sock(client,":%s!%s@%s MODE %s -b *!*@%s\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,chan_name,s_ip);
                        send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s -b *!*@%s\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,
                                        chan_name,s_ip);
                }
                if (mask_type == MASK_BOTH)
                {
                        write_sock(client,":%s!%s@%s MODE %s -b %s!*@%s\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,chan_name,nick,
                                s_ip);
                        send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s -b %s!*@%s\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,
                                        chan_name,nick,s_ip);
                }
                if (mask_type == MASK_EVERYBODY)
                {
                        write_sock(client,":%s!%s@%s MODE %s -b *!*@*\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,client->md5_ip,chan_name);
                        send_to_all_in_chan(client,chan_name,":%s!%s@%s MODE %s -b *!*@*\r\n",(chanserv) ? CHANSERV_NAME : client->nick,(chanserv) ? SERVER_NAME : client->md5_ip,
                                            client->md5_ip,chan_name);
                }
        }
        free(nick);
        free(s_ip);
        return;
}

static bool is_already_banned(struct ban_list *pban_, veryshort mask_type, char *nick, char *s_ip)
{
        struct ban_list *pban=pban_;

        while (pban != NULL)
        {
                if (mask_type == MASK_NICK && compic(pban->nick,nick))
                        return true;

                if (mask_type == MASK_IP && compic(pban->md5_ip,s_ip))
                        return true;

                if (mask_type == MASK_BOTH && compic(pban->md5_ip,s_ip) && compic(pban->nick,nick))
                        return true;

                if (mask_type == MASK_EVERYBODY && pban->mask_type == MASK_EVERYBODY)
                        return true;

                pban=pban->next;
        }

        return false;
}

bool is_banned(CLIENTS *client, CHANS *pchans)
{
        struct ban_list *pban=pchans->bans;

        while (pban != NULL)
        {
                if (pban->mask_type == MASK_NICK && compic(pban->nick,client->nick))
                        return true;

                if (pban->mask_type == MASK_IP && compic(pban->md5_ip,client->md5_ip))
                        return true;

                if (pban->mask_type == MASK_BOTH && compic(pban->md5_ip,client->md5_ip) && compic(pban->nick,client->nick))
                        return true;

                if (pban->mask_type == MASK_EVERYBODY)
                        return true;

                pban=pban->next;
        }

        return false;
}
